// async ll2axi fifo

// DATA INFO
// width 256 -> 128
// depth 8

// LENGTH INFO
// width 11
// depth 6  ( 1 LENGTH -> 64~1518 BYTE -> 2~48 DATA )

module ll2axi_fifo_dma_rx #(
        parameter DMA_DWIDTH = 128 , // DMA Data Width
        parameter MEM_DWIDTH = 256 , // MEM Data Width
        parameter MEM_AWIDTH = 8   , // MEM Addr Width
        parameter LEN_DWIDTH = 11  , // LEN Data Width
        parameter LEN_AWIDTH = 6     // LEN Addr Width
    )(
        // SYS
        input  wclk      , // 312p5MHz PKT
        input  rclk      , // 100MHz DMA
        input  rstn_wclk , // Low Active
        input  rstn_rclk , // Low Active

        // MEM CTL
        input  [11 : 0] ram_dp_cfg_register ,

        // WCLK
        input                     capture_vld_i  , // no sample
        input  [MEM_DWIDTH-1 : 0] capture_data_i ,
        input  [LEN_DWIDTH-1 : 0] capture_len_i  ,
        input                     capture_en_i   , // no sample
        output                    capture_rdy_o  , // wire out

        // RCLK
        input                     rd_leng_en_i  , // no sample
        input                     rd_data_en_i  , // no sample
        output                    rd_leng_vld_o , // reg out
        output                    rd_dout_vld_o , // reg out
        output [LEN_DWIDTH-1 : 0] rd_leng_o     , // reg out
        output [DMA_DWIDTH-1 : 0] rd_dout_o     , // reg out
        output                    empty_leng_o  , // wire out
        output                    empty_data_o    // wire out
    );

localparam MEM_DEPTH = 1 << MEM_AWIDTH ;
localparam LEN_DEPTH = 1 << LEN_AWIDTH ;

wire [LEN_DWIDTH-1 : 0] rd_mem_leng_o ;
wire [MEM_DWIDTH-1 : 0] rd_mem_dout_o ;
wire                    al_full_o     ;
//======================================================================================================
// LEN Input
reg                     capture_en_ff     ;
wire                    capture_en_pos    ;
reg                     capture_en_pos_ff ; // wr_leng_en
reg  [LEN_DWIDTH-1 : 0] capture_len_ff    ; // wr_leng_din

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        capture_en_ff     <= 1'b0 ;
        capture_en_pos_ff <= 1'b0 ;
    end
    else begin
        capture_en_ff     <= capture_en_i   ;
        capture_en_pos_ff <= capture_en_pos ;
    end
end

assign capture_en_pos = capture_en_i & ( !capture_en_ff ) ;

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        capture_len_ff <= 'd0 ;
    end
    else if ( capture_en_pos ) begin
        capture_len_ff <= capture_len_i ;
    end
    else begin
        capture_len_ff <= capture_len_ff ;
    end
end

//======================================================================================================
// LEN Output
reg [LEN_DWIDTH-1 : 0] rd_mem_leng_ff ;

always @( posedge rclk or negedge rstn_rclk ) begin
    if ( !rstn_rclk ) begin
        rd_mem_leng_ff <= 'd0 ;
    end
    else begin
        rd_mem_leng_ff <= rd_mem_leng_o ;
    end
end

assign rd_leng_o = rd_mem_leng_ff ;

//======================================================================================================
// Data Input
reg                    capture_vld_ff  ; // wr_data_en
reg [MEM_DWIDTH-1 : 0] capture_data_ff ; // wr_data_din

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        capture_vld_ff <= 1'b0 ;
    end
    else begin
        capture_vld_ff <= capture_vld_i ;
    end
end

always @( posedge wclk or negedge rstn_wclk ) begin
    if ( !rstn_wclk ) begin
        capture_data_ff <= 'd0 ;
    end
    else if ( capture_vld_i ) begin
        capture_data_ff <= capture_data_i ;
    end
    else begin
        capture_data_ff <= capture_data_ff ;
    end
end

//======================================================================================================
// DATA Output
reg                     odd_flag        ;
reg                     odd_flag_ff1    ;
reg                     odd_flag_ff2    ;
reg                     rd_data_en_ff   ;
wire                    rd_data_en_last ;
wire                    rd_dval         ;
reg                     rd_dval_ff      ;
reg  [MEM_DWIDTH-1 : 0] rd_mem_dout_ff  ;

always @( posedge rclk or negedge rstn_rclk ) begin
    if ( !rstn_rclk ) begin
        rd_data_en_ff <= 1'b0 ;
        odd_flag_ff1  <= 1'b0 ;
        odd_flag_ff2  <= 1'b0 ;
        rd_dval_ff    <= 1'b0 ;
    end
    else begin
        rd_data_en_ff <= rd_data_en_i ;
        odd_flag_ff1  <= odd_flag     ;
        odd_flag_ff2  <= odd_flag_ff1 ;
        rd_dval_ff    <= rd_dval      ;
    end
end

assign rd_data_en_last = ( !rd_data_en_i ) & rd_data_en_ff ;

always @( posedge rclk or negedge rstn_rclk ) begin
    if ( !rstn_rclk ) begin
        odd_flag <= 1'b1 ;
    end
    else begin
        case ( {rd_data_en_ff, rd_data_en_last} )
            2'b10: odd_flag <= ~odd_flag ;
            2'b11: odd_flag <= 1'b1 ;
          default: odd_flag <= odd_flag ;
        endcase
    end
end

assign rd_dval = rd_data_en_ff & odd_flag ;

always @( posedge rclk or negedge rstn_rclk ) begin
    if ( !rstn_rclk ) begin
        rd_mem_dout_ff <= 'd0 ;
    end
    else if ( rd_dval_ff ) begin
        rd_mem_dout_ff <= rd_mem_dout_o ;
    end
    else begin
        rd_mem_dout_ff <= rd_mem_dout_ff ;
    end
end

assign rd_dout_o = ( odd_flag_ff2 ) ? rd_mem_dout_ff[255:128] : rd_mem_dout_ff[127:0] ;

//======================================================================================================
// RD_VLD
localparam LATENCY_LENG = 2 ;
localparam LATENCY_DOUT = 3 ;

reg [LATENCY_LENG-1 : 0] rd_leng_vld_buffer ;
reg [LATENCY_DOUT-1 : 0] rd_dout_vld_buffer ;

always @( posedge rclk or negedge rstn_rclk ) begin
    if ( !rstn_rclk ) begin
        rd_leng_vld_buffer <= { LATENCY_LENG{1'b0} } ;
        rd_dout_vld_buffer <= { LATENCY_DOUT{1'b0} } ;
    end
    else begin
        rd_leng_vld_buffer <= { rd_leng_vld_buffer[LATENCY_LENG-2 : 0], rd_leng_en_i } ;
        rd_dout_vld_buffer <= { rd_dout_vld_buffer[LATENCY_DOUT-2 : 0], rd_data_en_i } ;
    end
end

assign rd_leng_vld_o = rd_leng_vld_buffer[LATENCY_LENG-1] ;
assign rd_dout_vld_o = rd_dout_vld_buffer[LATENCY_DOUT-1] ;

//======================================================================================================
// STATUS Output
assign capture_rdy_o = !al_full_o;

//======================================================================================================
// Instance
`ifdef VCS_MODEL
// vcs_model async native fifo
async_native_fifo #( .DW(LEN_DWIDTH), .AW(LEN_AWIDTH) ) len_fifo(
        .wclk      ( wclk          ),
        .wr_rstn   ( rstn_wclk     ),
        .rclk      ( rclk          ),
        .rd_rstn   ( rstn_rclk     ),

        .wr_en_i   ( capture_en_pos_ff ),
        .wr_din_i  ( capture_len_ff    ),
        .full_o    (                   ),

        .rd_en_i   ( rd_leng_en_i  ),
        .rd_dout_o ( rd_mem_leng_o ),
        .empty_o   ( empty_leng_o  )
    );

async_native_fifo #( .DW(MEM_DWIDTH), .AW(MEM_AWIDTH) ) data_fifo(
        .wclk      ( wclk      ),
        .wr_rstn   ( rstn_wclk ),
        .rclk      ( rclk      ),
        .rd_rstn   ( rstn_rclk ),

        .wr_en_i   ( capture_vld_ff  ),
        .wr_din_i  ( capture_data_ff ),
        .full_o    (                 ),

        .rd_en_i   ( rd_dval       ),
        .rd_dout_o ( rd_mem_dout_o ),
        .empty_o   ( empty_data_o  )
    );

assign al_full_o = 1'b0 ;

`elsif FPGA
// xilinx fifo generator 13.2
// native fifo / independent clk / block ram
// standard fifo
async_native_fifo_len len_fifo(
        // width = 11
        .wr_clk ( wclk      ),
        .rd_clk ( rclk      ),
        .rst    ( rstn_wclk ),

        .wr_en  ( capture_en_pos_ff ),
        .din    ( capture_len_ff    ),
        .full   (                   ),

        .rd_en  ( rd_leng_en_i  ),
        .dout   ( rd_mem_leng_o ),
        .empty  ( empty_leng_o  ),

        .wr_rst_busy ( ),
        .rd_rst_busy ( )
    );

async_native_fifo_data data_fifo(
        // width = 256
        .wr_clk ( wclk      ),
        .rd_clk ( rclk      ),
        .rst    ( rstn_wclk ),

        .wr_en       ( capture_vld_ff  ),
        .din         ( capture_data_ff ),
        .full        (                 ),
        .almost_full ( al_full_o       ),

        .rd_en  ( rd_dval       ),
        .dout   ( rd_mem_dout_o ),
        .empty  ( empty_data_o  ),

        .wr_rst_busy ( ),
        .rd_rst_busy ( )
    );

`else
wire       leng_fifo_wr_en   ;
wire [5:0] leng_fifo_wr_addr ;
wire [5:0] leng_fifo_rd_addr ;
wire       data_fifo_wr_en   ;
wire [7:0] data_fifo_wr_addr ;
wire [7:0] data_fifo_rd_addr ;

DW_fifoctl_s2_sf #(
        .depth(LEN_DEPTH),
        .push_ae_lvl(2),
        .push_af_lvl(2),
        .pop_ae_lvl(2),
        .pop_af_lvl(2),
        .err_mode(0),
        .rst_mode(0),
        .push_sync(2),
        .pop_sync(2),
        .tst_mode(0)
    ) leng_fifo_CTL (
        .clk_push(wclk),
        .clk_pop(rclk),
        .rst_n(rstn_wclk),
        .push_req_n(~capture_en_pos_ff), // push
        .pop_req_n(~rd_leng_en_i), // pop
        .we_n(leng_fifo_wr_en), // wr_en to ram
        .push_empty(),
        .push_ae(),
        .push_hf(),
        .push_af(), // wr_afull
        .push_full(), // wr_full
        .push_error(),
        .pop_empty(empty_leng_o), // rd_empty
        .pop_ae(),
        .pop_hf(),
        .pop_af(),
        .pop_full(),
        .pop_error(),
        .wr_addr(leng_fifo_wr_addr), // wr_addr to ram
        .rd_addr(leng_fifo_rd_addr), // rd_addr to ram
        .push_word_count(),
        .pop_word_count(),
        .test(1'b0)
    );
tp_rf_dp_d64_w11 U_tp_rf_dp_d64_w11(
    .clk_w (wclk),              //write clk
    .clk_r (rclk),              //read clk
    .rst_n (rstn_wclk),
    .wen   (~leng_fifo_wr_en),              //active-HIGH
    .waddr (leng_fifo_wr_addr),
    .wdata (capture_len_ff),
    .ren   (rd_leng_en_i),              //active-HIGH
    .raddr (leng_fifo_rd_addr),
    .rdata (rd_mem_leng_o)
    );
//ram_dp_d64_w11_wrapper U_ram_dp_d64_w11_wrapper(
//        .clka(wclk),
//        .clkb(rclk),
//        .ram_dp_cfg_register(ram_dp_cfg_register),
//        //we write enable,write,active high
//        .wea(~leng_fifo_wr_en),
//        .cena(1'b1),
//        .web(1'b0),
//        .cenb(rd_leng_en_i),
//        .addra(leng_fifo_wr_addr),
//        .addrb(leng_fifo_rd_addr),
//        .dina(capture_len_ff), // wr_data
//        .dinb(11'd0),
//        .douta(),
//        .doutb(rd_mem_leng_o) // rd_data
//    );

DW_fifoctl_s2_sf #(
        .depth(MEM_DEPTH),
        .push_ae_lvl(2),
        .push_af_lvl(2),
        .pop_ae_lvl(2),
        .pop_af_lvl(50),
        .err_mode(0),
        .rst_mode(0),
        .push_sync(2),
        .pop_sync(2),
        .tst_mode(0)
    ) data_fifo_CTL (
        .clk_push(wclk),
        .clk_pop(rclk),
        .rst_n(rstn_wclk),
        .push_req_n(~capture_vld_ff), // push
        .pop_req_n(~rd_dval), // pop
        .we_n(data_fifo_wr_en), // wr_en to ram
        .push_empty(),
        .push_ae(),
        .push_hf(),
        .push_af(al_full_o), // wr_afull
        .push_full(), // wr_full
        .push_error(),
        .pop_empty(empty_data_o), // rd_empty
        .pop_ae(),
        .pop_hf(),
        .pop_af(),
        .pop_full(),
        .pop_error(),
        .wr_addr(data_fifo_wr_addr), // wr_addr to ram
        .rd_addr(data_fifo_rd_addr), // rd_addr to ram
        .push_word_count(),
        .pop_word_count(),
        .test(1'b0)
    );
ram_dp_d256_w256_wrapper U_ram_dp_d256_w256_wrapper(
        .clka(wclk),
        .clkb(rclk),
        .ram_dp_cfg_register(ram_dp_cfg_register),
        //we write enable,write,active high
        .wea(~data_fifo_wr_en),
        .cena(1'b1),
        .web(1'b0),
        .cenb(rd_dval),
        .addra(data_fifo_wr_addr),
        .addrb(data_fifo_rd_addr),
        .dina(capture_data_ff), // wr_data
        .dinb(256'd0),
        .douta(),
        .doutb(rd_mem_dout_o) // rd_data
    );
`endif

endmodule
